//
//  PrivacyPolicyViewChina.swift
//
//  Created by volantPig on 2023/2/15.
//  Copyright © Jaguar Land Rover Ltd 2023 All rights reserved.
//  This software is confidential and proprietary information.
//

import UIKit
import CoreText
import CoreGraphics

typealias SelectLinkStringCompeleted = (String) -> Void

class LinkLabelModel: NSObject {
    var linkString: String?
    var linkUrl: String?
    var linkRange: NSRange?
    var linkStrAttributes: [NSAttributedString.Key: Any]?
}

class LinkLabel: UILabel {

    var selectLinkStringCompeleted: SelectLinkStringCompeleted?
    var linkStringInfoArray: [LinkLabelModel]!

    fileprivate var baseString: String?
    fileprivate var baseAttributes: [NSAttributedString.Key: Any]?

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func configureLinkInfo(_ baseString: String, _ baseAttributes: [NSAttributedString.Key: Any], _ linkSringArray: [LinkLabelModel]) {
        self.baseString = baseString
        self.baseAttributes = baseAttributes
        self.linkStringInfoArray = linkSringArray

        let mutableAttributeStr = NSMutableAttributedString.init(string: baseString)
        mutableAttributeStr.addAttributes(baseAttributes, range: NSRange(location: 0, length: baseString.count))
        for currentModel in self.linkStringInfoArray {
            guard let confirmAttDic = currentModel.linkStrAttributes, let linkStr = currentModel.linkString else {
                return
            }
            let allRange = baseString.findAllIndex(linkStr)
            if allRange.count > 0 {
                currentModel.linkRange = allRange.first
                mutableAttributeStr.addAttributes(confirmAttDic, range: allRange.first!)
            }
        }
        self.attributedText = mutableAttributeStr
        self.isUserInteractionEnabled = true
    }

    func refreshConfigureInfo(_ linkSringArray: [LinkLabelModel]) {
    }


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        let touch = touches.first
        if let point = touch?.location(in: self) {
            self.touchPoint(point: point)
        }
    }

    private func touchPoint(point: CGPoint) {
        guard let confirmAttText = self.attributedText else { return }
        let framesetter = CTFramesetterCreateWithAttributedString(confirmAttText)
        var path = CGMutablePath()
        path.addRect(self.bounds)
        var frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, confirmAttText.length), path, nil)
        let range = CTFrameGetVisibleStringRange(frame)

        if confirmAttText.length > range.length {

            var currentFont = UIFont()
            if let myFont = confirmAttText.attribute(.font, at: 0, effectiveRange: nil) {
                currentFont = myFont as! UIFont
            } else {
                if self.font != nil {
                    currentFont = self.font
                } else {
                    currentFont = UIFont.systemFont(ofSize: 1)
                }
            }
            path = CGMutablePath()
            path.addRect(CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height+currentFont.lineHeight))
            frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
        }
        let lines = CTFrameGetLines(frame)
        let count = CFArrayGetCount(lines)
        let numberOfLines = self.numberOfLines > 0 ? min(self.numberOfLines, count) : count

        if numberOfLines == 0 {
            return
        }
        let origins = UnsafeMutablePointer<CGPoint>.allocate(capacity: count)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), origins)
        let transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.0, -1.0)
        let verticalOffset: CGFloat = 0

        for index in 0..<numberOfLines {
            let line = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
            var ascent: CGFloat = 0.0
            var descent: CGFloat = 0.0
            var leading: CGFloat = 0.0

            let width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
            let height = ascent + abs(descent*2) + leading

            let flippedRect = CGRect(x: point.x, y: point.y, width: width, height: height)

            var rect = CGRectApplyAffineTransform(flippedRect, transform)
            rect = rect.insetBy(dx: 0, dy: 0)
            rect = rect.offsetBy(dx: 0, dy: verticalOffset)

            let style = confirmAttText.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle

            let lineSpace: CGFloat = style?.lineSpacing ?? 0.0

            let lineOutSpace = (self.bounds.size.height - lineSpace * CGFloat((count - 1)) - rect.size.height * CGFloat(count))/2
            rect.origin.y = lineOutSpace + rect.size.height * CGFloat(index) + lineSpace * CGFloat(index)

            if rect.contains(point) {
                let relativePoint = CGPoint(x: point.x, y: point.y)
                var index = CTLineGetStringIndexForPosition(line, relativePoint)
                var offset: CGFloat = 0.0
                CTLineGetOffsetForStringIndex(line, index, &offset)

                if offset > relativePoint.x {
                    index = index - 1
                }

                for currentModel in self.linkStringInfoArray {
                    guard let linkRange = currentModel.linkRange else { return }
                    if NSLocationInRange(index, linkRange) {
                        self.selectLinkStringCompeleted?(currentModel.linkString ?? "")
                    }
                }
            }
        }
    }
}
